Service container
https://laravel.com/docs/5.7/container
https://readouble.com/laravel/5.7/ja/container.html
Laravelにおいてクラスの依存関係の管理やDIを行うツール
Service locatorパターンで構成されてるkadoyau.icon
利用しているクラス群がどのインタフェースにも依存していないなら、コンテナにbindする必要はない
コンテナはどのようにオブジェクトをビルドすればいいのか教えてもらう必要がないので、リフレクションを使ってオブジェクトを自動的に解決できる
具体的にコードのどこ?kadoyau.icon
https://github.com/laravel/framework/blob/d651f8bd25c6baf7ae4913bc51f02849fad4e925/src/Illuminate/Container/Container.php#L785
コンテナへのオブジェクトの登録(Binding)と取得(Resolve)の方法
ほとんどすべてのサービスコンテナはService providersの中でregisterされるので、以降Service providersの中でコンテナを使う例とする
Service Providersのregister()で利用されている
Binding
インタフェースと実装を紐付ける
Bindingの基礎
Simple Bindings
Service Providerにおいては常に$this->appを通じてコンテナにアクセスできる
$this->app->bind()を使ってbindingを登録することが出来る
登録したいクラスまたはインタフェースの名前と、そのクラスインスタンスを返すClosure (PHP)を一緒に渡す
code:php
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
引き渡すclousureの引数にコンテナ自身($app)をうけとることができる。これを使ってオブジェクト構築時に必要な依存性を解決することができる
Bilding A Singleton
Singletonなオブジェクトを作ることが出来る
具体例:Laravelの初期化時にも利用されている
Binding Instances
既にインスタンス化したオブジェクトをbindすることもできる
Binding Primitives
インスタンスではなくプリミティブも受け渡せる
code:php
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName') // UserControllerのコンストラクタで受け取りたいプリミティブ
->give($value);
プリミティブもクラスも両方受け渡したいときは?kadoyau.icon
実装にインタフェースをbindする
sevice containerの強力な機能
インタフェースを与えられた実装にbindすることができる
例:EventPusherインタフェースとRedisEventPusherの実装を持っていたとする
$app->bind()でサービスコンテナにregisterできる
code:php
$this->app->bind(
'App\Contracts\EventPusher', // インタフェース
'App\Services\RedisEventPusher' //実装クラス
);
これでクラスがEventPusherの実装を必要としたときRedisEventPusherのインスタンスが注入されるようになる
Contextual Binding
同じインタフェースを利用した別のクラスに、異なった実装を注入したい場合
code:php
$this->app->when(PhotoController::class)
->needs(Filesystem::class) // PhotoControllerはFilesystemに依存している
->give(function () {
return Storage::disk('local'); // ローカルにアクセスしてほしい
});
$this->app->when(VideoController::class, UploadController::class) // これらもFilesystemに依存しているが
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3'); // S3にアクセスする
});
Tagging
特定のbindingのカテゴリすべてを解決したいときに使う
例:多数の異なるReportインタフェースの実装の配列を受け取る、ReportAggregator(array $reports)を構築したいとき
Reportの実装をregisterした後にそれらをtag()を使ってタグ付けすることができる
code:php
$this->app->bind('SpeedReport', function () {
//
});
$this->app->bind('MemoryReport', function () {
//
});
$this->app->tag('SpeedReport', 'MemoryReport', 'reports'); // 複数のReportsの実装にrepostsタグをつけた
一度タグ付けされれば、$app->tagged()を呼ぶことでタグが付いたものを一度にresolveできる
code:php
$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports')); // SpeedReportとMemoryReportがインスタンス化された配列が渡る
});
Extending Buindings
後付で拡張できる
Resolving
バインドしたオブジェクトを取得する方法
make()を使う
$app->make()でコンテナからクラスのインスタンスを解決できる
引数には解決したいクラスまたはインタフェースの名前を渡すことが出来る
code:php
$api = $this->app->make('HelpSpot\API');
$appにアクセスできない場所からはresolve()ヘルパメソッドを使うことが出来る
code:php
$api = resolve('HelpSpot\API');
クラスの依存性がコントローラから解決不能な場合、$app->makeWith()をつかって連想配列としてinjectすることもできる
code:php
$api = $this->app->makeWith('HelpSpot\API', 'id' => 1);
Automatic Injection
クラスのコンストラクタのタイプヒントをつかって依存性を注入することができる
controller, event listener, queue jobs, middlewareなどで利用することができる
Container Events
サービスコンテナはオブジェクトを解決するたびにイベントを発火する
このイベントは$this->app->resolving()でlistenすることができる
その他
PSR-11を実装しているのでLaravel containerのインスタンスを取得するためにPSR-11のコンテナインタフェースのタイプヒントが利用できる